package hk.edu.cityu.is.riskmgmt.algorithm.interbank;

import java.util.Random;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;

public class InterPaymentConstructor {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger
			.getLogger(InterPaymentConstructor.class);

	public static void main(String[] args) {
		BasicConfigurator.configure();
		int bankNO = 16;

		int[][] InitialInterbankMatrix = new int[bankNO][bankNO];
		double[][] InitialInterpayWeight = new double[bankNO][bankNO];

		InterbankMatrixConstructor b = new InterbankMatrixConstructor();
		b.setInterbankMatrix(bankNO, InitialInterbankMatrix);

		double[] cash = { 1000000, 3000000, 6000000, 2000000, 7000000, 8000000,
				1000000, 1000000, 4000000, 5000000, 7000000, 1000000, 1000000,
				3000000, 4000000, 4000000 };

		int linkNO = bankNO * (bankNO - 1) / 2;
		int[] I_Index, J_Index;
		I_Index = new int[linkNO];
		J_Index = new int[linkNO];
		double[] WeightArray = new double[linkNO];
		InterPaymentConstructor a = new InterPaymentConstructor();
		a.setInitialInterpayWeight(bankNO, linkNO, cash,
				InitialInterbankMatrix, InitialInterpayWeight, I_Index,
				J_Index, WeightArray);

		for (int i = 0; i < bankNO; i++) {
			for (int j = 0; j < bankNO; j++) {
				if (logger.isInfoEnabled()) {
					logger.info("main(String[]) - " + (int) InitialInterpayWeight[i][j] + ".0t"); //$NON-NLS-1$ //$NON-NLS-2$
				}
			}
			if (logger.isInfoEnabled()) {
				logger.info("main(String[])"); //$NON-NLS-1$
			}
		}
		if (logger.isInfoEnabled()) {
			logger.info("main(String[]) - ======================================================"); //$NON-NLS-1$
		}
		for (int i = 0; i < linkNO; i++) {
			if (logger.isInfoEnabled()) {
				logger.info("main(String[]) - " + (int) WeightArray[i] + ".0t"); //$NON-NLS-1$ //$NON-NLS-2$
			}
		}
		if (logger.isInfoEnabled()) {
			logger.info("main(String[])"); //$NON-NLS-1$
		}
		if (logger.isInfoEnabled()) {
			logger.info("main(String[]) - ======================================================"); //$NON-NLS-1$
		}

		double[][] SecondInterpayWeight = new double[bankNO][bankNO];
		a.setInterpayWeight(bankNO, linkNO, cash, InitialInterbankMatrix,
				I_Index, J_Index, WeightArray, SecondInterpayWeight);
		for (int i = 0; i < bankNO; i++) {
			for (int j = 0; j < bankNO; j++) {
				if (logger.isInfoEnabled()) {
					logger.info("main(String[]) - " + (int) SecondInterpayWeight[i][j] + ".0t"); //$NON-NLS-1$ //$NON-NLS-2$
				}
			}
			if (logger.isInfoEnabled()) {
				logger.info("main(String[])"); //$NON-NLS-1$
			}
		}
	}

	/**
	 * 
	 * @param bankNO
	 * @param linkNO
	 * @param cashReserve
	 * @param InitialInterbankMatrix
	 * @return InitialInterpayWeight
	 * @return I_Index
	 * @return J_Index
	 * @return WeightArray
	 */
	public void setInitialInterpayWeight(int bankNO, int linkNO,
			double[] cashReserve, int[][] InitialInterbankMatrix,
			double[][] InitialInterpayWeight, int[] I_Index, int[] J_Index,
			double[] WeightArray) {
		int i, j, counter;
		Random rand = new Random();

		counter = 0;
		for (i = 0; i < bankNO; i++) {
			for (j = 0; j < bankNO; j++) {
				if (InitialInterbankMatrix[i][j] != 0) {
					I_Index[counter] = i;
					J_Index[counter] = j;
					counter++;
				}
			}
		}// Index the bank pairs of payment

		counter = 0;
		while (counter < linkNO) {
			// generate an appropriate payment value
			double tempvalue = -1;
			while ((tempvalue <= 0) || (tempvalue >= 8.857)) {
				tempvalue = rand.nextGaussian() * 1.5027 + 4.4285;
				// mu 4.4285
				// sigma 1.5027
			}
			tempvalue = Math.pow(10, tempvalue) / 1000;

			for (int t = 0; t < linkNO; t++) {
				if (WeightArray[t] == 0) {
					if (satisfied(tempvalue, I_Index, J_Index, cashReserve, t)) {
						WeightArray[t] = tempvalue;
						counter++;
						break;
					}// /TBD
				} else { // WeightArray[t]!=0, so try to hash the value.
					if (WeightArray[t] < tempvalue) {
						if (satisfied(tempvalue, I_Index, J_Index, cashReserve,
								t)
								&& WeightHash(WeightArray, I_Index, J_Index,
										cashReserve, t, linkNO))// /TBD
						{
							WeightArray[t] = tempvalue;
							counter++;
							break;
						}
					}
				}
			}
		}// try to fit the value into the array

		for (i = 0; i < linkNO; i++)
			// turn array into matrix
			InitialInterpayWeight[I_Index[i]][J_Index[i]] = WeightArray[i];

		// Now, sort the weight vector and corresponding indices
		int t1, t2;
		double t3;
		for (i = linkNO - 1; i >= 0; i--) {
			for (j = 0; j < i; j++) {
				if (WeightArray[j] > WeightArray[j + 1]) {
					t1 = I_Index[j];
					t2 = J_Index[j];
					t3 = WeightArray[j];

					I_Index[j] = I_Index[j + 1];
					J_Index[j] = J_Index[j + 1];
					WeightArray[j] = WeightArray[j + 1];

					I_Index[j + 1] = t1;
					J_Index[j + 1] = t2;
					WeightArray[j + 1] = t3;
				}
			}
		}// bubble sorting

	}// end of function setInitialInterpayWeight

	private boolean WeightHash(double[] WeightArray, int[] I_Index,
			int[] J_Index, double[] cashReserve, int t, int total) {
		boolean flag = false;
		for (int i = 0; i < total; i++)
			if ((WeightArray[i] == 0)
					&& (satisfied(WeightArray[t], I_Index, J_Index,
							cashReserve, t))) {
				WeightArray[i] = WeightArray[t];
				flag = true;
				break;
			}
		return flag;
	}

	/**
	 * 
	 * @param value
	 * @param I_Index
	 * @param J_Index
	 * @param cashReserve
	 * @param t
	 * @return
	 */
	private boolean satisfied(double value, int[] I_Index, int[] J_Index,
			double[] cashReserve, int t) {
		if ((value >= (0.05 * cashReserve[I_Index[t]]))
				&& (value <= (0.3 * cashReserve[I_Index[t]]))
				&& (value <= (0.2 * cashReserve[J_Index[t]]))
				&& (value >= (0.05 * cashReserve[J_Index[t]])))
			return true;
		else
			return false;
	}

	/**
	 * 
	 * @param bankNO
	 * @param cashReserve
	 * @param InitialInterbankMatrix
	 * @param I_Index
	 * @param J_Index
	 * @param WeightArray
	 * @return NextInterpayWeight
	 */
	public void setInterpayWeight(int bankNO, int linkNO, double[] cashReserve,
			int[][] InitialInterbankMatrix, int[] I_Index, int[] J_Index,
			double[] WeightArray, double[][] NextInterpayWeight) {
		int i, j, counter;
		Random rand = new Random();

		// make some variation on the OriginalInterbankMatrix
		int[][] InterbankMatrix = new int[bankNO][bankNO];
		for (i = 0; i < bankNO; i++) {
			for (j = 0; j < bankNO; j++) {
				if ((InitialInterbankMatrix[i][j] == 1)
						&& (rand.nextInt(10) < 8)) {
					InterbankMatrix[i][j] = 1;
				}
			}
		}

		double[] tempArray = new double[linkNO];
		counter = 0;
		while (counter < linkNO) {
			// generate an appropriate payment value
			double tempvalue = -1;
			while ((tempvalue <= WeightArray[0])
					|| (tempvalue >= WeightArray[linkNO - 1])) {
				tempvalue = rand.nextGaussian() * 1.5027 + 4.4285;
				// mu 4.4285
				// sigma 1.5027
				tempvalue = Math.pow(10, tempvalue) / 1000;
			}
			tempArray[counter++] = tempvalue;
		}

		double temp;
		for (i = linkNO - 1; i >= 0; i--) {
			for (j = 0; j < i; j++) {
				if (tempArray[j] > tempArray[j + 1]) {
					temp = tempArray[j];
					tempArray[j] = tempArray[j + 1];
					tempArray[j + 1] = temp;
				}
			}
		}// // bubble sorting

		for (i = 0; i < linkNO; i++) {
			if (InterbankMatrix[I_Index[i]][J_Index[i]] == 1)
				NextInterpayWeight[I_Index[i]][J_Index[i]] = tempArray[i];
		}
	}// end of function setInterpayWeight
}